#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_log.h"

#include "mqtt_client.h"
#include "tmc2209.h"
#include "led.h"
#include "motor_mqtt.h"

static const char *TAG = "motor_mqtt";
static bool connected = false;
static esp_mqtt_client_handle_t client;

const char *hass_config_topic       = "homeassistant/cover/cover2mqtt01/config";
const char *hass_availability_topic = "homeassistant/cover/cover2mqtt01/availability";
const char *hass_setpos_topic       = "homeassistant/cover/cover2mqtt01/set_position";
const char *hass_set_topic          = "homeassistant/cover/cover2mqtt01/set";
const char *hass_pos_topic          = "homeassistant/cover/cover2mqtt01/position";
const char *hass_state_topic        = "homeassistant/cover/cover2mqtt01/state";

const char *ha_conf = "{ \n\
   \"name\": \"cover2mqtt\",\n\
   \"state_topic\":\"homeassistant/cover/cover2mqtt01/state\",\n\
   \"unique_id\":\"cover2mqtt01\",\n\
   \"command_topic\": \"homeassistant/cover/cover2mqtt01/set\",\n\
   \"payload_open\": \"OPEN\",\n\
   \"payload_close\": \"CLOSE\",\n\
   \"payload_stop\": \"STOP\",\n\
   \"position_topic\": \"homeassistant/cover/cover2mqtt01/position\",\n\
   \"set_position_topic\": \"homeassistant/cover/cover2mqtt01/set_position\",\n\
   \"position_open\": 0,\n\
   \"position_closed\": 100,\n\
   \"availability_topic\": \"homeassistant/cover/cover2mqtt01/availability\",\n\
   \"payload_available\": \"online\",\n\
   \"payload_not_available\": \"offline\",\n\
   \"device\":{\n\
      \"identifiers\":[\n\
         \"01cover2mqtt\"\n\
      ],\n\
      \"name\":\"MQTT Cover Step\"\n\
   }\n\
}";

static void log_error_if_nonzero(const char *message, int error_code)
{
    if (error_code != 0) {
        ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
    }
}

void mqtt_publish_state(uint8_t state)
{
    const char *state_str;

    if (!connected) return;

    switch (state)
    {
    case MQTT_STATE_STOP:
        state_str = "stopped";
        break;
    case MQTT_STATE_OPEN:
        state_str = "opening";
        break;
    case MQTT_STATE_OPENED:
        state_str = "open";
        break;
    case MQTT_STATE_CLOSE:
        state_str = "closing";
        break;
    case MQTT_STATE_CLOSED:
        state_str = "closed";
        break;
    default:
        return;
    }

    esp_mqtt_client_publish(client, hass_state_topic, state_str, 0, 1, 0);
}

void mqtt_publish_pos(uint8_t pos)
{
    char posstr[6];

    if (!connected) return;

    sprintf(posstr, "%d", pos);
    ESP_LOGI(TAG, " publish pos MQTT_EVENT_DATA %s", posstr);

    esp_mqtt_client_publish(client, hass_pos_topic, posstr, 0, 1, 0);
}


static void mqtt_set(const char *buff)
{
    tmc_queue_t tmcq;
    memset(&tmcq, 0, sizeof(tmc_queue_t));

    if (strstr(buff, "OPEN")) {
        tmcq.type = TMC_TARGET;
        tmcq.data.val = 0;
        xQueueSend(tmc_queue, &tmcq, pdMS_TO_TICKS(200));
        led_queuesend_state(LED_FLASH);
    } else if (strstr(buff, "CLOSE")) {
        tmcq.type = TMC_TARGET;
        tmcq.data.val = 100;
        xQueueSend(tmc_queue, &tmcq, pdMS_TO_TICKS(200));
        led_queuesend_state(LED_FLASH);
    } else if (strstr(buff, "STOP")) {
        tmcq.type = TMC_STOP;
        xQueueSend(tmc_queue, &tmcq, pdMS_TO_TICKS(200));
        led_queuesend_state(LED_FLASH);
    }

    return;
}

static void mqtt_setpos(const char *buff)
{
    tmc_queue_t tmcq;
    memset(&tmcq, 0, sizeof(tmc_queue_t));

    uint8_t slider_val = atoi(buff);

    ESP_LOGI(TAG, " set pos MQTT_EVENT_DATA %d", slider_val);
    tmcq.type = TMC_TARGET;
    tmcq.data.val = slider_val;
    xQueueSend(tmc_queue, &tmcq, pdMS_TO_TICKS(200));
    led_queuesend_state(LED_FLASH);
}

static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id) {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
        msg_id = esp_mqtt_client_publish(client, hass_config_topic, ha_conf, 0, 1, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_publish(client, hass_availability_topic, "online", 0, 1, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, "homeassistant/status", 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, hass_setpos_topic, 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, hass_set_topic, 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        char buff[10] = { 0 };
        sprintf(buff, "%d", get_cur_pos_per());
        msg_id = esp_mqtt_client_publish(client, hass_pos_topic, buff, 0, 1, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

        if (!get_tmc_status()) {
            msg_id = esp_mqtt_client_publish(client, hass_state_topic, "STOP", 0, 1, 0);
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        }

        connected = true;
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");

        connected = false;
        break;

    case MQTT_EVENT_SUBSCRIBED:
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);

        if (strstr(event->topic, hass_setpos_topic)) {
            mqtt_setpos(event->data);
        }
        else if (strstr(event->topic, hass_set_topic)) {
            mqtt_set(event->data);
        }

        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
            log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
            log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
            log_error_if_nonzero("captured as transport's socket errno",  event->error_handle->esp_transport_sock_errno);
            ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));

        }
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

static void mqtt_task(void* arg)
{    
    tmc_queue_t tmcq;

    while (1) {
        if (xQueueReceive(tmc_queue, &tmcq, portMAX_DELAY) == pdTRUE) {
            tmc_queue_prase(&tmcq);
        } else {
            ESP_LOGE(TAG, "Failed to receive event");
        }
    }
}

void mqtt_app_start(void)
{
    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = "mqtt://192.168.31.102:1883",
        //.broker.address.port = 1883,

        .credentials.username = "hassmqtt",
        .credentials.authentication.password = "hassmqtt8888",
    };

    client = esp_mqtt_client_init(&mqtt_cfg);
    /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);

    xTaskCreate(mqtt_task, "mqtt_task", 8192, NULL, 8, NULL);
}